#include <functional>

template <typename R>
const typename R::first_argument_type& select_0_2(const typename R::first_argument_type& a, 
	const typename R::second_argument_type& b, R r)
{
	if(r(b,a)) return b; // if b < a
	return a; // if not b < a => a <= b
}

template <typename R>
const typename R::first_argument_type& select_1_2(const typename R::first_argument_type& a,
	const typename R::second_argument_type& b, R r)
{
	if(r(b,a)) return a; // if b < a
	return b; //if not b < a => a <= b
}

template <typename R>
const typename R::first_argument_type& select_0_3(const typename R::first_argument_type& a,
	const typename R::first_argument_type& b, 
	const typename R::first_argument_type& c, R r)
{
	return select_0_2(select_0_2(a,b,r),c,r);
}

template <typename R>
const typename R::first_argument_type& select_2_3(const typename R::first_argument_type& a,
	const typename R::first_argument_type& b, 
	const typename R::first_argument_type& c, R r)
{
	return select_1_2(select_1_2(a,b,r),c,r);
}

template <typename R>
const typename R::first_argument_type& select_1_3_ab(const typename R::first_argument_type& a,
	const typename R::first_argument_type& b, 
	const typename R::first_argument_type& c, R r)
{
	if(!r(c,b)) return b; //if not c < b => if b <= c
	return select_1_2(a,c,r); //else c < b, select between a and c
}

template <typename R>
const typename R::first_argument_type& select_1_3(const typename R::first_argument_type& a,
	const typename R::first_argument_type& b, 
	const typename R::first_argument_type& c, R r)
{
	if( r(b,a) ) 
		return select_1_3_ab(b,a,c,r);
	return select_1_3_ab(a,b,c,r);
}

template <typename R>
const typename R::first_argument_type& select_1_4_ab_cd(const typename R::first_argument_type& a,
	const typename R::first_argument_type& b, 
	const typename R::first_argument_type& c,
	const typename R::first_argument_type& d, R r)
{
	if (r(c,a)) 
		return select_0_2(a,d,r);
	return select_0_2(b,c,r);
}

template <typename R>
const typename R::first_argument_type& select_1_4_ab(const typename R::first_argument_type& a,
	const typename R::first_argument_type& b, 
	const typename R::first_argument_type& c,
	const typename R::first_argument_type& d, R r)
{
	if (r(d,c)) 
		return select_1_4_ab_cd(a,b,d,c,r);
	return select_1_4_ab_cd(a,b,c,d,r);
}

template <typename R>
const typename R::first_argument_type& select_1_4(const typename R::first_argument_type& a,
	const typename R::first_argument_type& b, 
	const typename R::first_argument_type& c,
	const typename R::first_argument_type& d, R r)
{
	if (r(b,a)) 
		return select_1_4_ab(b,a,c,d,r);
	return select_1_4_ab(a,b,c,d,r);
}

template <typename R>
const typename R::first_argument_type& select_2_4_ab_cd(const typename R::first_argument_type& a,
	const typename R::first_argument_type& b, 
	const typename R::first_argument_type& c,
	const typename R::first_argument_type& d, R r)
{
	if (r(c,a)) 
		return select_1_2(a,d,r);
	return select_1_2(b,c,r);
}

template <typename R>
const typename R::first_argument_type& select_2_4_ab(const typename R::first_argument_type& a,
	const typename R::first_argument_type& b, 
	const typename R::first_argument_type& c,
	const typename R::first_argument_type& d, R r)
{
	if (r(d,c)) 
		return select_2_4_ab_cd(a,b,d,c,r);
	return select_2_4_ab_cd(a,b,c,d,r);
}

template <typename R>
const typename R::first_argument_type& select_2_4(const typename R::first_argument_type& a,
	const typename R::first_argument_type& b, 
	const typename R::first_argument_type& c,
	const typename R::first_argument_type& d, R r)
{
	if (r(b,a)) 
		return select_2_4_ab(b,a,c,d,r);
	return select_2_4_ab(a,b,c,d,r);
}



